package dryven.view.engine.parser.parts;

import java.io.IOException;
import java.io.Writer;

import dryven.codegen.asm.MethodBuilder;
import dryven.view.ViewDataDictionary;
import dryven.view.engine.component.ViewComponent;
import dryven.view.engine.component.childzone.ChildZoneBindings;
import dryven.view.engine.component.metadata.AttributeDeclaration;
import dryven.view.engine.component.metadata.ViewComponentMetaDataStrategy;
import dryven.view.engine.expression.ViewDataDeclaration;
import dryven.view.engine.expression.nodelisters.ExpressionNodeFactory;
import dryven.view.engine.parser.CompilationException;
import dryven.view.engine.parser.ViewComponentLocator;
import dryven.view.engine.parser.ViewDataDeclarationCollection;
import dryven.view.engine.parser.componentinvocation.AttributeBinding;
import dryven.view.engine.parser.componentinvocation.AttributeBindings;
import dryven.view.engine.parser.componentinvocation.ChildZoneBindingsImpl;
import dryven.view.engine.parser.scope.InstructionScope;

public class ComponentInvocationPart implements CompiledViewPart {

	private AttributeBindings _attributesBindings;
	private ViewComponentMetaDataStrategy _strategy;
	private ChildZoneBindingsImpl _childZoneBindings = new ChildZoneBindingsImpl();
	
	public ComponentInvocationPart(AttributeBindings attributesBindings,
			ViewComponentMetaDataStrategy strategy) {
		super();
		_attributesBindings = attributesBindings;
		_strategy = strategy;
	}

	@Override
	public void evaluate(ViewDataDictionary viewData, Writer out, ViewDataDeclarationCollection declarations)
			throws IOException {
		ViewComponent component = _strategy.getComponentInstance();
		component.renderComponent(viewData, out, _childZoneBindings, _attributesBindings);
	}

	@Override
	public void writeInstructions(MethodBuilder builder) {
		// TODO Auto-generated method stub

	}

	@Override
	public void compile(ViewDataDeclarationCollection declarations, ViewComponentLocator locator) throws CompilationException {
		_attributesBindings.compile(declarations);
		_strategy.compile(declarations,locator);
		
		for (AttributeDeclaration aDecl : _strategy.getAttributeDeclarations()) {
			AttributeBinding aBinding = _attributesBindings.getBindingByName(aDecl.getName());
			if(aBinding==null) {
				if(aDecl.isRequired()) {
					throw new RuntimeException(String.format("Required attribute %s for component %s missing", aDecl.getName(),_strategy.getComponentName()));
				}
				else {
					continue;
				}
			}
			if(!aDecl.getValueType().isAssignableFrom(aBinding.getBoundType())) {
				throw new RuntimeException(
						String.format("Invalid attribute type for %s. Is %s but should be something assignable to %s", 
								aDecl.getName(),
								aBinding.getBoundType().getName(),
								aDecl.getValueType().getName()));
			}
		}
		
		for (ChildZonePart cz : _childZoneBindings.getParts()) {
			cz.compile(declarations, locator);
		}
	}
	
	public ChildZonePart getOrAddChildZonePart(String name, InstructionScope parentScope, ExpressionNodeFactory _expressionFactory) {
		ChildZonePart part = _childZoneBindings.getChildZonePart(name);
		if(part==null) {
			part = new ChildZonePart(parentScope);
			_childZoneBindings.setChildZonePart(name, part);
		}
		return part;
	}

}
